<--- %%NOBANNER%% --> survtd.sas
 BackForward

/*------------------<--- Start of Description -->--------------------\
| This macro will calculate the general survival statistics (p(t),   |
| standard error, confidience limits, and median survival time, for  |
| the following special cases:                                       |
|   1) Left-truncated survival;                                      |
|   2) Time dependant calculations where a person can change state   |
|      (CLASS) after time zero.                                      |
| This macro does not do any testing. Use Procedures phreg, cox, etc.|
| This macro, also, does not support multiple event data.            |
|--------------------<--- End of Description -->---------------------|
|--------------------------------------------------------------------|
|--------------<--- Start of Files or Arguments Needed -->-----------|
| The Input Parameters are:                                          |
|   STRTTIME = Variable containing the beginning time.  See examples |
|            above. (Required).                                      |
|   STOPTIME = Variable containing the ending time.  See examples    |
|            above. (Required)                                       |
|   EVENT =  Variable containing the event status at STOPTIME, as a  |
|            numeric two-valued variable,                            |
|            (0,1),  (1,2) etc.  The event value must be 1 larger    |
|            than the censoring value. (Required),                   |
|   CEN_VL = Censoring value for the EVENT variable as 0,1 etc.      |
|            Event = Cen_vl + 1.  (Default is 0)                     |
|   CLASS = Classification or state variable(s).  They may be either |
|            character or numeric.  Note - Any observations in the   |
|            input dataset with  missing CLASS data, are not included|
|            in the results.                                         |
|   MINTIME = the beginning time for the suvival calculations and    |
|            x-axis of the graph (Defualt is 0).  Indicate MANTIME in|
|            the same units as STRTTIME, even if XDIVISOR is coded.  |
|   BY = List of "by" variables.  They may be either character or    |
|            numeric.                                                |
|   OUT = Output dataset name (Default is _SURVOUT)                  |
|   OUTSUM = Output summary dataset name (Default is _SURVSUM)       |
|   DATA = Input dataset name (Default is the last dataset created)  |
|   PRINTOP = printing options (Default is 1):                       |
|             0 = print nothing                                      |
|             1 = print summary table only                           |
|             2 = print one line per event                           |
|             3 = print one line per event, arrival, and/or censor   |
|             4 = print one line for each of a series of time points,|
|                  as months, half-years, or years (see POINTS)      |
|             5 = print one line per event and/or defined time points|
|             6 = print one line per event,arrival, censor, and/or   |
|                 time point.                                        |
|   POINTS = Specific time points at which survival statistics are   |
|            needed, as months, half-years, years.  These points are |
|            specified by dividing time into intervals as:           |
|                     '0 to 36500 by 365'.                           |
|            The endpoint of each interval will be the time point to |
|            be reported.  If you have comas within your statement,  |
|            enclose the entire parameter in quotes, as:             |
|                     '0 to 360 by 30, 0 to 3650 by 182.5'           |
|            You may also specify specific points as well as         |
|            groups of points as:                                    |
|                     '1,5,10,15,0 to 36500 by 182.5'                |
|   CL = Type of confidence limits (Default is 3)                    |
|             1 = Greenwood (actual)                                 |
|             2 = Greenwood with modified lower limit                |
|             3 = log-e transformation (log)                         |
|             4 = log-e transformation with modified lower limit     |
|             5 = log(-log-e) transformation (log(-log))             |
|             6 = log(log-e) transformation with modified lower limit|
|             7 = logit transformation (logit)                       |
|             8  =logit transformation with modified lowere limit    |
|   ALPHA= Type I error rate for confidence limits (Default is .05)  |
|   MEDTYPE = Type of median if there are several time points        |
|            having probability=.5  (Default is 1)                   |
|             1 = use the midpoint between the times as the median   |
|             2 = use the first time value as the median             |
|   PLOTTYPE = Where to plot the graph(s) (Default is 1)             |
|             1 = no plot                                            |
|             2 = greenbar printer plot                              |
|             3 = graphics plot on unix laser printer                |
|             4 = plot goes to graphics window                       |
|   PLOTOP = What to plot on the y-axis (Default is 1)               |
|             1 = plot pt                                            |
|             2 = plot 1-pt or pe                                    |
|   SCALE = plotting scale (Default is 1)                            |
|             1 = arithmetic                                         |
|             2 = 1-cycle log                                        |
|             3 = 2-cycle log                                        |
|   MAXTIME = the maximun time allowed for the x-axis (Default is the|
|            max time for all graphs per page). Specify MAXTIME in   |
|            the same units as STRTTIME, even if XDIVISOR used.      |
|   XDIVISOR = the divisor used if you want the plotted x-axis in    |
|            other units then TIME is in.                            |
|       Example:  XDIVISOR=365 would plot the x-axis as TIME/365.    |
|   LASERPRT = the name of the HSR printer you want your plot to go  |
|            to if different from your standard printer.             |
| Output Dataset OUT:                                                |
|   Output Dataset (OUT) contains one observation for each event,    |
|   censor, arrival, and extra time point specified by POINTS.       |
|   The variables in the output dataset are:                         |
|   &by = the by-variable(s) (if defined);                           |
|   &class = the class variable(s) (if defined);                     |
|   &stoptime = the stoptime variable;                               |
|   NRISK = the number at risk at &stoptime;                         |
|   NEVENT = the number of events from &stoptime the next stoptime   |
|   NCENSOR = the number of censors from &stoptime the next stoptime |
|   NARRIVE = the number of arrivals during this time;               |
|   CUM_EV = the cumulative number of events up to and including     |
|          &stoptime;                                                |
|   CUM_CEN = The cumulative number of censors up to and including   |
|          &stoptime;                                                |
|   CUM_ARR = The cumulative number of arrivals up to and including  |
|          this time;                                                |
|   PT = The probability of no event up to and including &stoptime.  |
|   PE = 1-PT, or the probability of an event occcurring;            |
|   UPPER_CL = the upper confidience limit (based on the input       |
|          parameters ALPHA and CL);                                 |
|   LOWER_CL = the lower confidience limit (based on the input       |
|          parameters ALPHA and CL);                                 |
|   SE = the Greenwood Standard Error;                               |
|   POINTFLG = the flag indicating points added to the output dataset|
|          because the of POINTS option.  (1=point added,            |
|          missing otherwise);                                       |
|   Output Dataset OUTSUM:                                           |
|   Output  Summary Dataset (OUTSUM) contains one observation for    |
|   each group processed. That is, the total group, or each BY       |
|   and/or CLASS value.  The variables in the output dataset are:    |
|   &by = the by-variable(s) (if defined);                           |
|   &class = the class variable(s) (if defined);                     |
|   TOTAL = the total number of observations in this group;          |
|   CUM_EV = the total number of events in this group;               |
|   CUM_CEN = the total number of censors in this group;             |
|   CUM_ARR = the total number of arrivals in this group;            |
|   TL_MISS = the total number of observations not included because  |
|          of missing values;                                        |
|   MEDIAN = the median survival time (based on the input parameter  |
|          MEDTYPE);                                                 |
|   The following variables will be added if the LOGRANK test is     |
|   specified:                                                       |
|   OBSERVED = the calculated number of observed events;             |
|   EXPECTED = the calculated number of expected events;             |
|   RR = the Relative Risk (this group's observed/expected / group   |
|         1's observed/expected);                                    |
|   CHISQ = chi-square value;                                        |
|   DF = degrees of freedom;                                         |
|   PVALUE = pvalue (probability of a greater chi-square value);     |
| Notes:                                                             |
|   1. If you are getting a message about VPOS not being large       |
|      enough, try cutting down on the number of title lines you are |
|      using. SAS does it's calculations for size based on 1 title,  |
|      so having 3 or 4 titles MAY cause a problem with the vertical |
|      spacing.                                                      |
|   2. If you are plotting the output dataset yourself, remember that|
|      you need a symbol statement as follows to get the steps       |
|      correct:                                                      |
|            symbol1 i=stepjl v=none l=1;                            |
|---------------<--- End of Files or Arguments Needed -->------------|
|--------------------------------------------------------------------|
|----------------<--- Start of Example and Usage -->-----------------|
| Left-truncation:                                                   |
|   An example of left-truncation is a study where surivial needs    |
|   to be measured from initial diagnosis until death and there are  |
|   several patients in the study diagnosed elsewhere prior to coming|
|   to Mayo.  Patients diagnosed at Mayo contribute to the survival  |
|   curve from date of diagnosis (t(0)), but those "arriving" at Mayo|
|   years (t(a)) after diagnosis contribute to the curve starting    |
|   after time t(a).                                                 |
|   This macro will produce a survival curve using patients in       |
|   the curve from the time they "arrive" until last                 |
|   follow-up.  To do this you need a dataset with one observation   |
|   per person with the following variables:                         |
|   STRTTIME = Variable containing the time (in any units) when      |
|              the patient "arrives" measured from the time of       |
|              initial interest (as initial diagnosis date in the    |
|              above examp) If the patient "arrives" at time t(0)    |
|              (the patient was diagnosed in Rochester) STRTTIME     |
|              would be 0.                                           |
|   STOPTIME = Variable containing the follow-up time measured from  |
|              the time of initial interest (as initial diagnosis    |
|              date in the above example).                           |
|   *** Note - STRTTIME and STOPTIME cannot be the same value. In    |
|              some cases you may need to add a small value (0.5)    |
|              to either the strttime or stoptime values to make     |
|              them different.                                       |
|   EVENT = variable containing the survival status at STOPTIME.     |
|                                                                    |
| Example: A person diagnosed in Rochester then dying on day 35:     |
|          STRTTIME=0, STOPTIME=35, EVENT=1                          |
| Example: A person diagnosed elsewhere, coming to Rochester on      |
|          day 35 then dying on day 200:                             |
|          STRTTIME=35, STOPTIME=200, EVENT=1                        |
| Example: A person diagnosed in Rochester then dying soon after:    |
|          STRTTIME=0.1, STOPTIME=0.9, EVENT=1                       |
| Time Dependent Co-variates:                                        |
|   An example of a time dependent covariate is a study where a      |
| patient was in one state (CLASS) for a length of time (as without a|
| transplant), then moves into another state (CLASS) (as transplant).|
| Survival is measured from some initial time (as date placed on the |
| waiting list for a transplant) until last follow-up or death.      |
|   This macro will produce a survival curve for each state of the   |
| time-dependent covariate (CLASS) (one for no transplant,and one for|
| transplants, in the above example). Patients may, or may not, be in|
| more than one of the curves, but they can only be in 1 curve at any|
| given time (day, or day, hour and minute).  Any person transferring|
| from one state to another would have two observations, one for time|
| in state 1 and another for the time in state 2.  The EVENT variable|
| would always be censored in the first observation.                 |
| There is no limit to the number of transfers a patient can make.   |
|                                                                    |
|   To do this you need a dataset with one observation per           |
| person per "state" with the following variables:                   |
|   CLASS = Variable containing the state or value of the            |
|         covariate from STRTTIME to STOPTIME.  In the above example |
|         CLASS will have 2 values, 0=no transplant and              |
|         1=transplanted.                                            |
|   STRTTIME = Variable containing the starting time (in any units)  |
|            for the patient in this state. If the patient is in     |
|            this state at t(0) (date put on the transplant list)    |
|            STRTTIME would be 0.                                    |
|   STOPTIME = Variable containing the ending time (in any units)    |
|            for the patient in this state.  If the patient dies, or |
|            is censored this would be the last follow-up time.      |
| *** Note - STRTTIME and STOPTIME cannot be the same value.         |
|            In some cases you may need to add a small value         |
|            (0.5) to either the strttime or stoptime values to make |
|            them different.                                         |
|   EVENT = variable containing the survival status at STOPTIME.     |
| EXample: A person on the transplant and never receive a transplant |
|          and dying on day 35 would have one observation with:      |
|            CLASS=0, STRTTIME=0, STOPTIME=35, EVENT=1               |
| Example:  A person on the transplant list, transplanted on day 15, |
|           an dying on day 45 would have two observations, one for  |
|           each class or state:                                     |
|           CLASS=0, STRTTIME=0, STOPTIME=15, EVENT=0                |
|           CLASS=1, STRTTIME=15.5, STOPTIME=45, EVENT=1             |
| Note: For both types of analysis, events occurring at t(0) should  |
|       be recorded as STRTTIME=0.1 STOPTIME=0.9.                    |
| Note: For both types of analysis, use the MINTIME variable to begin|
|       your survival calculations at some time later than t(0). This|
|       may be necessary if you have small numbers of people at risk |
|       in the first units of time.                                  |
| Examples:                                                          |
|   %survtd(strttime=roch_dt, stoptime=fu_time, event=fu_stat,       |
|           cen_vl=1);                                               |
|   %survtd(strttime=arrtime,stoptime=fu_time,event=fu_stat,cen_vl=1,|
|            out=two,data=one,printop=4,cl=6, mintime=30,            |
|            points='0 to 36500 by 182.5');                          |
|   %survtd(strttime=arrtime,stoptime=fu_time,event=fu_stat,cen_vl=1,|
|             class=chf,out=two,data=one,printop=4,cl=6,             |
|             points='0 to 36500 by 182.5');                         |
| Usage: %SURVTD(STRTTIME= ,STOPTIME= ,EVENT= ,CEN_VL=0, PRINTOP=1,  |
|                CLASS= ,BY= , MINTIME=0, DATA=&syslast,POINTS= ,    |
|                OUT=_SURVOUT,CL=3,ALPHA=.05,PLOTTYPE=1,PLOTOP=1,    |
|                SCALE=1,MAXTIME=, XDIVISOR= ,LASERPRT= ,MEDTYPE=1,  |
|                OUTSUM=_SURVSUM);                                   |
\-------------------<--- End of Example and Usage -->---------------*/
%MACRO SURVTD  (STRTTIME= ,STOPTIME= ,EVENT= ,CEN_VL=0, PRINTOP=1,
                CLASS= ,BY= , MINTIME=0,
                DATA=&syslast, OUT=_SURVOUT,POINTS= ,CL=3,
                ALPHA=.05,PLOTTYPE=1,PLOTOP=1,SCALE=1,MAXTIME= ,
                XDIVISOR= ,LASERPRT= ,MEDTYPE=1,OUTSUM=_SURVSUM);
/*--------------------------------------------\
| Author:  Jan Offord;                        |
| Purpose: calculate the general survival     |
|          statistics for left truncated data;|
\--------------------------------------------*/
RUN;

proc sql;
reset noprint;
select max(number) into :t from dictionary.titles;
quit;

%let t=%eval(&t+2);
%if &t > 9 %then %let t=9;
%let u=%eval(&t+1);


%local byword byclword lastby lastbycl dev errorflg j a b x cgrp
     cl_name indata;

%LET errorflg = 0;
%LET byword =  ;
%LET byclword = ;
%LET lastby = ;
%LET lastbycl = ;
%LET dev = &SYSDEVIC;
%let a = %index(&points,%str(%'));
%if &a > 0 %then %do;
   %let b = %eval(%length(&points)-1);
   %let points = %substr(&points,%eval(&a+1),%eval(&b-1));
   %end;

%if &strttime=  %then %do;
   %put  ERROR - Variable  not defined;
   %LET  errorflg = 1;
   %end;

%if &stoptime=  %then %do;
   %put  ERROR - Variable  not defined;
   %LET  errorflg = 1;
   %end;

 %if &event=  %then %do;
   %put  ERROR - Variable  not defined;
   %LET  errorflg = 1;
   %end;

%if &cen_vl ^= 0 and &cen_vl ^= 1 %then %do;
   %put  ERROR - Variable  not defined as 0 or 1;
   %LET  errorflg = 1;
   %end;

%if &printop < 0 or &printop > 6 %then %do;
   %put  ERROR - Variable  not defined as 0-6;
   %LET  errorflg = 1;
   %end;

 %if &printop >= 4 and &printop <= 6 and &points =  %then %do;
   %put  ERROR - Variable  = 4,5,6, but  missing;
   %LET  errorflg = 1;
   %end;

 %if &plottype<1 or &plottype>4  %then %do;
   %put  ERROR - Variable  not 1,2,3, or 4;
   %LET  errorflg = 1;
   %end;

 %if &plotop<1 or &plotop>2  %then %do;
   %put  ERROR - Variable  not 1 or 2;
   %LET  errorflg = 1;
   %end;

 %if &scale<1 or &scale>3  %then %do;
   %put  ERROR - Variable  not 1, 2, or 3;
   %LET  errorflg = 1;
   %end;

 %if &laserprt^=  and &plottype^=3  %then %do;
   %put  ERROR - Variable   is present, but  = 3, but running on an IBM machine;
   %LET  errorflg = 1;
   %end;

 %if &plottype=4 and &sysenv=BACK  %then %do;
   %put  ERROR - Variable  = 4, but running in backgroupd;
   %LET  errorflg = 1;
   %end;

 %if &medtype<1 or &medtype>2  %then %do;
   %put ERROR - Variable  is not 1 or 2;
   %let errorflg = 1;
   %end;

 %if &cl<1 or &cl>8  %then %do;
   %put ERROR - Variable  is not 1-8;
   %let errorflg = 1;
   %end;

%if &errorflg = 1 %then %do;
    data _null_;
    error 'ERROR - detected in the input data to the macro .';
    %go to exit;
    %end;

%IF &BY^=  %THEN %DO;
   %LET byword=BY;
   %LET byclword=BY;
   %DO J=1 %TO 50 %BY 1;    /*  find last BY variable  */
      %LET X=%SCAN(&BY,&J);
      %IF &X^=  %THEN %do;
         %LET lastby=&X;
         %LET lastbycl=&X;
         %end;
      %ELSE %GOTO NEXT1;
      %END;
   %NEXT1:  %END;

%IF &CLASS^=  %THEN %DO;
   %LET byclword=BY;
   %DO J=1 %TO 50 %BY 1;    /*  find last CLASS variable  */
      %LET X=%SCAN(&CLASS,&J);
       %IF &X^=  %THEN %do;
         %LET lastbycl=&X;
          %let V&j = &X;
          %let cgrp = &j;
          %local V&j;
          %end;
         %ELSE %GOTO NEXT2;
      %END;
   %NEXT2:
    %END;

data _tmp_;  set &data;
     keep &by &class &strttime &stoptime &event;

   %if &class^=  %then %do;
    %do j=1 %to &cgrp %by 1;
      where &&v&j is  not missing;
      %end;
     %end;

  if &strttime=. or &strttime < 0 then do;
     error "ERROR - &strttime= " &strttime ' - not used.';
     &strttime = .;
     &stoptime = .;
     &event = .;
     end;

%if &stoptime^=  %then %do;
  if &stoptime=. or &stoptime < 0 then do;
     error "ERROR - &stoptime= " &stoptime ' - not used.';
     &stoptime = .;
     &strttime = .;
     &event = .;
     end;

  if &strttime>=&stoptime then do;
     error "ERROR - &strttime>=&stoptime " ' - not used.';
     &strttime = .;
     &stoptime = .;
     &event = .;
     end;

  if &event > &cen_vl+1 or &event < &cen_vl then do;
     error "ERROR - &event= " &event ' - not used.';
     &strttime = .;
     &stoptime = .;
     &event = .;
     end;
   %end;


PROC SORT data=_tmp_; BY  &by &class &stoptime ;
PROC MEANS NOPRINT DATA=_TMP_; VAR  &stoptime ; &byclword &by &class;
    OUTPUT OUT=_COUNTS_ N=nrisk max=maxtime nmiss=tl_miss;

data _arrive_;
   set _tmp_;
   keep &by &class &stoptime arrival;
   &stoptime=&strttime;
   arrival=1;
data _tmp_;
   set _tmp_ _arrive_;
proc sort;  by &by &class &stoptime;

%IF &POINTS^=  %THEN %DO;
   data _TMP1_;    /*  add point observations  */
       set _COUNTS_;
       keep &by &class &stoptime  &event point narr;
       flag=0;
       do j=&points;
         if flag=1 then return;
         if j>=maxtime then flag=1;
         if j>0 then do;
             &stoptime  = j;
            &event = .;
            point = 1;
            output;
            end;
         end;

   data _TMP_;  set _TMP_ _TMP1_;
   proc sort;  by &BY &CLASS  &stoptime ;
   %END;


DATA &OUT (KEEP=&by &CLASS  &stoptime nrisk nevent ncensor narrive
             cum_ev cum_cen cum_arr
             pt pe upper_cl lower_cl se pointflg)
     &outsum (keep=&by &class total cum_ev cum_cen cum_arr median
              tl_miss)
     _print_ (keep=&by &class  &stoptime  years nrisk nevent pt
              ncensor narrive
              lower_cl upper_cl se cum_ev cum_cen cum_arr);
  SET _TMP_ nobs=nobs; BY &by &class  &stoptime ;
   RETAIN pt nevent _kt_ ncensor nrisk narrive _sv1_ cum_ev cum_cen
      total median firstmed pointflg laster loweradj cum_arr oldpt;
   LABEL pt="Kaplan-Meier Survival Estimate"
      pe = "1-P(t)"
      se="Greenwold Standard Error"
      lower_cl  ="Lower Confidence Limit"
      upper_cl  ="Upper Confidence Limit"
      NRISK="Number at Risk at beginning of (t)"
      NEVENT="Number of Events at (t)"
      ncensor="Number Censored at (t)"
      narrive="Number of Arrivals at (t)"
      cum_ev = "Cumulative events including (t)"
      cum_cen = "Cumulative censors including (t)"
      cum_arr = "Cumulative arrivals including (t)"
      median = "Median Survival"
      tl_miss = "Total Missing"
      pointflg = "Added Times"
      ;

   _FT_=FIRST. &stoptime ;
   _LT_=LAST. &stoptime ;

   %if &points = %then %do;
      point=.;
      %end;

   %IF &lastbycl^=  %THEN %DO;
      IF FIRST.&lastbycl=0 THEN GO TO NOTFIRST;
     %END;
    %ELSE %DO;
      IF _N_>1 THEN GO TO NOTFIRST;
    %END;

    /* do if the first observation per by group */

  SET _COUNTS_;    /*  read observation from _counts_ */

   total = nrisk;
   nrisk=0;
   _thold_= &stoptime;
   laster=nrisk;
   pointflg=.;
   &stoptime =0;
   nevent=0;
   _kt_=0;
   ncensor=0;
   narrive=0;
   cum_ev=0;
   cum_cen=0;
   cum_arr=0;
   pt=1;
   oldpt = pt;
   pe=0;
   se=0;
   _sv1_=0;
   lower_cl=1;
   upper_cl=1;
   loweradj=.;
   years=0;
   OUTPUT &out;        /*  output an observation at time=0 */
   OUTPUT _print_;
    &stoptime =_THOLD_;
   median=.;
   firstmed=.;

    /*  do for each observation in the dataset */

  NOTFIRST:

   IF _FT_ then do;    /*  do for the first obs. per time */
      nevent=0;
      _kt_=0;
      ncensor=0;
      narrive=0;
      pointflg=.;
     end;

   /*  for each observation */

   if point = 1 then pointflg = 1;

   if point ^= 1 and arrival^=1 and  &stoptime  ^= . then do;
      if &event = &cen_vl+1 then NEVENT=NEVENT+1;
        else ncensor=ncensor+1;
      _KT_=_KT_+1;
      end;
   if arrival=1 then narrive = narrive + 1;

   IF _LT_ then do;     /* do for the last observation per time */

     if _kt_ = 0 and pointflg ^= 1 and narrive=0 then go to next3;

                         /* if time< mintime  */
     if &stoptime < &mintime then go to next3;

     if nrisk>0 then pt=pt*(1-NEVENT/NRISK);
            else pt=oldpt;
     oldpt = pt;
     pe = 1 - pt;
     cum_ev = cum_ev + nevent;
     cum_cen = cum_cen + ncensor;
     cum_arr = cum_arr + narrive;
     if nevent>0 then laster=nrisk;  /*  nrisk at last event time */

     IF nrisk<=nevent THEN DO;
       se=.;
       lower_cl=.;
       upper_cl=.;
       END;
      ELSE DO;
       if _kt_>0 then loweradj=SQRT(laster/nrisk);
       _sv1_=_sv1_+nevent/nrisk/(nrisk-nevent);
       se=SQRT(pt*pt*_sv1_);

       %if &cl=1 %then %do;
          %let cl_name=Greenwood;
          lower_cl=pt-PROBIT(1-&alpha/2)*se;
          upper_cl=pt+PROBIT(1-&alpha/2)*se;
          %end;
       %if &cl=2 %then %do;
          %let cl_name=Green(adj);
          lower_cl=pt-PROBIT(1-&alpha/2)*loweradj*se;
          upper_cl=pt+PROBIT(1-&alpha/2)*se;
          %end;

       %if &cl=3 %then %do;
          %let cl_name=Log;
          _w_=PROBIT(1-&alpha/2)*sqrt(_sv1_);
          lower_cl=exp(log(pt)-_w_);
          upper_cl=exp(log(pt)+_w_);
          %end;
       %if &cl=4 %then %do;
          %let cl_name=Log(adj);
         _wl_=PROBIT(1-&alpha/2)*loweradj*sqrt(_sv1_);
          _w_=PROBIT(1-&alpha/2)*sqrt(_sv1_);
          lower_cl=exp(log(pt)-_wl_);
          upper_cl=exp(log(pt)+_w_);
          %end;

      %if &cl=5 %then %do;
          %let cl_name=Log(-log);
          if pt^=1 then do;
            _w_=PROBIT(1-&alpha/2)*sqrt(_sv1_)/log(pt);
            lower_cl=pt**exp(-_w_);
            upper_cl=pt**exp(_w_);
            end;
           else do;
            lower_cl=1;
            upper_cl=1;
            end;
          %end;
       %if &cl=6 %then %do;
          %let cl_name=Log(-log)adj;
          if pt^=1 then do;
            _wl_=PROBIT(1-&alpha/2)*loweradj*sqrt(_sv1_)/log(pt);
            _w_=PROBIT(1-&alpha/2)*sqrt(_sv1_)/log(pt);
            lower_cl=pt**exp(-_wl_);
            upper_cl=pt**exp(_w_);
            end;
           else do;
            lower_cl=1;
            upper_cl=1;
            end;
          %end;

      %if &cl=7 %then %do;
          %let cl_name=Logit;
          if pt^=1 then do;
             _w_=PROBIT(1-&alpha/2)*sqrt(_sv1_/(1-pt)**2);
             _zl_=exp(log(pt/(1-pt))-_w_);
             _zu_=exp(log(pt/(1-pt))+_w_);
             lower_cl=_zl_/(1+_zl_);
             upper_cl=_zu_/(1+_zu_);
            end;
           else do;
            lower_cl=1;
            upper_cl=1;
            end;
          %end;
       %if &cl=8 %then %do;
          %let cl_name=Logit(adj);
          if pt^=1 then do;
             _w_=PROBIT(1-&alpha/2)*sqrt(_sv1_/(1-pt)**2);
             _wl_=PROBIT(1-&alpha/2)*loweradj*sqrt(_sv1_/(1-pt)**2);
             _zl_=exp(log(pt/(1-pt))-_wl_);
             _zu_=exp(log(pt/(1-pt))+_w_);
             lower_cl=_zl_/(1+_zl_);
             upper_cl=_zu_/(1+_zu_);
            end;
           else do;
            lower_cl=1;
            upper_cl=1;
            end;
          %end;
        if lower_cl<0 then lower_cl=0;
        if upper_cl>1 then upper_cl=1;
        END;

     OUTPUT &out;

     years = round( &stoptime /365,0.01);
      %if &printop = 2 %then %do;
          if nevent > 0 then output _print_;
          %end;
      %if &printop = 3 %then %do;
          if nevent > 0  or ncensor > 0  or narrive > 0 then
             output _print_;
          %end;
      %if &printop = 4 %then %do;
          if pointflg = 1 then output _print_;
          if  &stoptime =0 and (nevent>0 or ncensor>0) then
              output _print_;
          %end;
      %if &printop = 5 %then %do;
          if nevent > 0  or pointflg = 1 then output _print_;
          %end;
      %if &printop = 6 %then %do;
          output _print_;
          %end;

     next3:
     NRISK=NRISK-_KT_;
     nrisk=nrisk+narrive;

     if _kt_ = 0  or pt=. or &stoptime<&mintime then go to next4;
     if ABS(pt-0.5)<=0.00001 then do;
        if firstmed = . then firstmed =  &stoptime ;
        end;
     if median=. and round(pt,0.00001) < 0.5  THEN DO;
        if firstmed ^=. then
             %if &medtype=1 %then  %do;
                median = ( &stoptime  + firstmed)/2.0;
                %end;
             %if &medtype=2 %then %do;
                 median = firstmed;
                 %end;
           else median =  &stoptime ;
        end;

     next4:                             /*  output summary data */
     %IF &lastbycl^=  %THEN %DO;
        IF LAST.&lastbycl=1 THEN output &outsum;
        %END;
       %ELSE %DO;
         IF _N_=nobs THEN output &outsum;
         %END;

     end;
run;

%if &printop = 0 %then %goto plots;

proc print data=&outsum split='*'; &byword &by;
   id  &class;
   var total cum_ev cum_cen cum_arr  tl_miss median;
   label total = Total*N
         tl_miss = Total*Missing
         cum_ev = Total*Events
         cum_cen = Total*Censors
         cum_arr = Total*Arrivals
         median = Median*Survival
        ;
   sum total cum_ev cum_cen tl_miss;
title&t
   "Survival Summary Table for Variables < &stoptime > and <&event>";
   title&u "With Arrivals  - Calculations starting at t(&mintime)";

footnote1 "Input Parameters (STRTTIME=&strttime,STOPTIME=&stoptime,"
        "EVENT=&event,CEN_VL=&cen_vl,"
        "PRINTOP=&printop,CLASS=&class,BY=&by,";
footnote2 "DATA=&data,OUT=&out,"
        " POINTS=&points,CL=&cl,ALPHA=&alpha,";
footnote3 "PLOTTYPE=&plottype,PLOTOP=&plotop,SCALE=&scale,MAXTIME="
             "&maxtime,XDIVISOR=&xdivisor,LASERPRT=&laserprt,"
             "MEDTYPE=&medtype,OUTSUM=&outsum,MINTIME=&mintime)";
  run;


%if &printop = 1 %then %goto plots;

data _NUll_;
         y=put(100-&alpha*100,2.);
        call symput('percent',y);


 footnote1;
 footnote2;
 footnote3;

proc print data=_print_ split='*';  &byclword &by &class;
        id  &stoptime  years;
   %if &printop = 2 %then %do;
        var nrisk nevent pt lower_cl upper_cl se;
        sum nevent;
        %end;
   %if &printop = 3 %then %do;
        var nrisk narrive nevent ncensor pt lower_cl upper_cl se;
        sum narrive nevent ncensor;
        %end;
   %if &printop = 4 %then %do;
        var nrisk cum_arr cum_ev cum_cen pt lower_cl upper_cl se;
        %end;
   %if &printop = 5 %then %do;
        var nrisk nevent pt lower_cl upper_cl se;
        sum nevent;
        %end;
   %if &printop = 6 %then %do;
        var nrisk narrive nevent ncensor pt lower_cl upper_cl se;
        sum narrive nevent ncensor;
        %end;
        format pt lower_cl upper_cl se 5.3;
        label &stoptime  = * &stoptime *(t)
              years = * &stoptime *'/365'
              nrisk = Number*'at Risk'*'at (t)'
              cum_ev = Cumulative*'# Events'*'<= (t)'
              narrive = Number*'Arrivals'*'at (t)'
              cum_arr = Cumulative*'# Arrivals'*'<= (t)'
              nevent = Number*'Events'*'at (t)'
              ncensor = Number*'Censors'*'at (t)'
              cum_cen = Cumulative*'# Censors'*'<= (t)'
              pt = Probability*'No Event'*'<= (t)'
              lower_cl = "Lower &percent%"*'C. Limit'*"(&cl_name)"
              upper_cl = "Upper &percent%"*'C. Limit'*"(&cl_name)"
              se = Greenwood*Standard*Error;
   %if &by ^= %then %do;
        pageby &lastby;
        %end;
title&t
  "Kaplan-Meier Survival Estimates for < &stoptime > and <&event>";
   title&u "With Arrivals  - Calculations starting at t(&mintime)";
       run;


    /*  plotting  */

%plots:
title&t;
title&u;

%if &plottype=1 %then %goto exit;

%if &plottype=2 %then %do;

%LET indata = &out;

PROC MEANS NOPRINT DATA=_counts_; &byword &by;
    VAR maxtime;
    OUTPUT OUT=_MAX_ max=maxt;

%if &maxtime ^=  %then %do;
  data _max_;  set _max_;
      if maxt>&maxtime then maxt=&maxtime;
  %end;

%if &xdivisor ^=  %then %do;
  data _max_;  set _max_;
      maxt=maxt/&xdivisor;
  data _tmp_;  set &out;
      &stoptime = &stoptime /&xdivisor;
  %let indata = _tmp_;
  %end;

data _tmp_;  set &indata;   &byword &by;
   keep &by x y symbol;
   retain maxt xtick ytick oldx oldy;
   if pt=. then delete;

   label x = " &stoptime ";
   label y = 'Percent';

   %if &xdivisor ^=  %then %do;
       label x = " &stoptime /&xdivisor";
       %end;

   %if &class ^=  %then %do;
      symbol=&class;
      label symbol = "&class";
      %end;
     %else %do;
      symbol='*';
      %end;

   %IF &lastby^=  %THEN %DO;
      IF FIRST.&lastby=0 THEN GO TO next4;
     %END;
    %ELSE %DO;
      IF _N_>1 THEN GO TO next4;
    %END;

  SET _Max_;
   xtick=maxt/100/2;
   ytick=1/30/2;

   %if  &sysscp=OS  %then %do;
       ytick=1/40/2;
       %end;

   oldx=.;
   oldy=.;
   pt=pt*100;
   go to next5;

 next4:
   pt=pt*100;
   if oldx ^=. and oldx >  &stoptime   then do;
                          /*  first obs for next class */
        oldx=.;
        oldy=.;
        go to next5;
        end;

   if  &stoptime >0 and nevent=0 and ncensor=0 then return;

   /*  horizonal dots */

   do i=oldx to  &stoptime -xtick by xtick while (i<=maxt);
        if i^=oldx then do;
           x=i;
           y=oldy;
           output;
           end;
        end;

    if oldy^=pt and  &stoptime <=maxt then do i=oldy to pt by -ytick;
           x= &stoptime ;
           y=i;
           output;
        end;

   next5:
   x= &stoptime ;
   y=pt;

   if x<=maxt then output;
   oldx=x;
   oldy=y;

%if &plotop = 2 %then %do;
    data _tmp_;  set _tmp_;  y=100-y;
    %end;

   proc plot data=_tmp_ nolegend uniform;  &byword &by;
       plot y*x=symbol
          /hzero hpos=100
       %if &sysscp=OS %then %do;
           vpos=40
           %end;
         %else %do;
            vpos=30
            %end;
       %if &scale=1 %then %do;
           vaxis=0 to 100 by 25;
           %end;
       %if &scale=2 %then %do;
           vaxis=10 17.78 31.62 56.233 100;
           %end;
       %if &scale=3 %then %do;
           vaxis=1 3.162 10 31.62 100;
           %end;
   footnote1 "TIME= &stoptime, EVENT=&event, CLASS=&class - "
           "With Arrivals  - Calculations starting at t(&mintime)";

   run;

   %end;

%if &plottype = 3 %then %do;

  %if &laserprt ^=  %then %do;
     filename graphout pipe "cat | /usr/ucb/lpr -P&laserprt ";
     %end;
    %else %do;
     filename graphout pipe 'cat | /usr/ucb/lpr ';
     %end;

/*  libname gdevice0  '/people/statsys/kosanke/mydevices'; */
  goptions cback=white colors=(black) device=apple;
  %end;

%if &plottype >= 3 %then %do;

  data _tmp_;
      set &out;
      keep x y &class &by;
      retain lastpt lastx;
      label y = 'Percent';

      if  &stoptime =0 then do;
        lastx=.;
        lastpt=.;
        end;

      if  &stoptime >0 and nevent=0 and ncensor=0 then delete;

      %if &maxtime^=  %then %do;
         if  &stoptime >&maxtime then do;
           if lastx=1 then delete;
             else do;
              lastx=1;
               &stoptime =&maxtime;
              pt=lastpt;
              end;
            end;
         %end;

      lastpt=pt;
      y=pt*100;
      %if &plotop = 2 %then %do;
        y=100-y;
        %end;

   %if &xdivisor ^=  %then %do;
       label x = " &stoptime /&xdivisor";
       x =  &stoptime /&xdivisor;
       %end;
     %else %do;
      label x = " &stoptime ";
      x= &stoptime ;
      %end;

  proc gplot data=_tmp_;  &byword &by;
   %if &class ^= %then %do;
      plot y*x=&class
      %end;
     %else %do;
        plot y*x
      %end;
          /hzero
       %if &scale=1 %then %do;
           vaxis=0 to 100 by 10;
           %end;
       %if &scale=2 %then %do;
           vaxis=10 17.78 31.62 56.233 100;
           %end;
       %if &scale=3 %then %do;
           vaxis=1 3.162 10 31.62 100;
           %end;
       symbol1 i=stepjl v=none l=1;
       symbol2 i=stepjl v=none l=2;
       symbol3 i=stepjl v=none l=4;
       symbol4 i=stepjl v=none l=8;
       symbol5 i=stepjl v=none l=41;
       symbol6 i=stepjl v=none l=33;
       symbol7 i=stepjl v=none l=35;
       symbol8 i=stepjl v=none l=46;
       symbol9 i=stepjl v=none l=40;
   footnote1 "TIME= &stoptime, EVENT=&event, CLASS=&class - "
           "With Arrivals  - Calculations starting at t(&mintime)";
     run;

    %end;

proc datasets nolist;
      %if &points ^=  %then %do;
            delete _tmp1_ _tmp_ _counts_ _print_;
            %end;
         %else %do;
            delete _tmp_ _counts_ _print_;
            %end;
    run;
    quit;
   footnote1;
%exit:

OPTIONS _LAST_=&out;

%let SYSDEVIC = &dev;

%MEND SURVTD;